home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
ip
/
ka9q
/
src.arc
/
8250.C
next >
Wrap
C/C++ Source or Header
|
1989-08-19
|
9KB
|
402 lines
/* OS- and machine-dependent stuff for the 8250 asynch chip on a IBM-PC
*
* 16550 support plus some statistics added mah@hpuviea.at 15/7/89
*/
#include <stdio.h>
#include <dos.h>
#include "global.h"
#include "iface.h"
#include "asy.h"
#include "pc.h"
#include "slip.h"
#include "proc.h"
#include "8250.h"
static int asyrxint __ARGS((unsigned dev));
static void asytxint __ARGS((unsigned dev));
static struct asy Asy[ASY_MAX];
unsigned Nasy;
/* ASY interrupt handlers */
static INTERRUPT (*Handle[])() = {asy0vec,asy1vec,asy2vec,asy3vec,asy4vec};
/* Initialize asynch port "dev" */
int
asy_init(dev,iface,arg1,arg2,bufsize)
int16 dev;
struct iface *iface;
char *arg1,*arg2; /* Attach args for address and vector */
unsigned bufsize;
{
register unsigned base;
register struct fifo *fp;
register struct asy *ap;
char i_state;
ap = &Asy[dev];
ap->iface = iface;
ap->addr = htoi(arg1);
ap->vec = htoi(arg2);
/* Set up receiver FIFO */
fp = &ap->fifo;
if((fp->buf = malloc(bufsize)) == NULLCHAR){
printf("asy%d: No space for rx buffer\r\n",dev);
return -1;
}
fp->bufsize = bufsize;
fp->wp = fp->rp = fp->buf;
fp->cnt = 0;
base = ap->addr;
/* Purge the receive data buffer */
(void)inportb(base+RBR);
i_state = dirps();
/* Save original interrupt vector, mask state, control bits */
ap->save.vec = getirq(ap->vec);
ap->save.mask = getmask(ap->vec);
ap->save.lcr = inportb(base+LCR);
ap->save.ier = inportb(base+IER);
ap->save.mcr = inportb(base+MCR);
/* save speed bytes */
setbit(base+LCR,LCR_DLAB);
ap->save.divl = inportb(base+DLL);
ap->save.divh = inportb(base+DLM);
clrbit(base+LCR,LCR_DLAB);
/* Set interrupt vector to SIO handler */
setirq(ap->vec,Handle[dev]);
/* Set line control register: 8 bits, no parity */
outportb(base+LCR,(char)LCR_8BITS);
/* determine if 16550, turn on FIFO mode and clear RX and TX FIFOs */
outportb(base+FCR,(char) FIFO_ENABLE);
if (inportb(base+IIR) & IIR_FIFO_ENABLED) {
ap->is_16550 = 1;
outportb(base+FCR,(char) FIFO_SETUP);
} else
ap->is_16550 = 0;
/* Turn on receive interrupt enable in 8250, leave transmit
* and modem status interrupts turned off for now
*/
outportb(base+IER,(char)IER_DAV);
/* Set modem control register: assert DTR, RTS, turn on 8250
* master interrupt enable (connected to OUT2)
*/
outportb(base+MCR,(char)(MCR_DTR|MCR_RTS|MCR_OUT2));
/* Enable interrupt */
maskon(ap->vec);
restore(i_state);
return 0;
}
int
asy_stop(iface)
struct iface *iface;
{
register unsigned base;
register struct asy *ap;
char i_state;
ap = &Asy[iface->dev];
base = ap->addr;
/* Purge the receive data buffer */
(void)inportb(base+RBR);
/* and hardware fifos if available */
if (ap->is_16550)
outportb(base+FCR,(char) FIFO_SETUP);
/* Restore original interrupt vector and 8259 mask state */
i_state = dirps();
setirq(ap->vec,ap->save.vec);
if(ap->save.mask)
maskon(ap->vec);
else
maskoff(ap->vec);
/* Restore speed regs */
setbit(base+LCR,LCR_DLAB);
outportb(base+DLL,ap->save.divl); /* Low byte */
outportb(base+DLM,ap->save.divh); /* Hi byte */
clrbit(base+LCR,LCR_DLAB);
/* Restore control regs */
outportb(base+LCR,ap->save.lcr);
outportb(base+IER,ap->save.ier);
outportb(base+MCR,ap->save.mcr);
restore(i_state);
return 0;
}
/* Asynchronous line I/O control */
int
asy_ioctl(iface,argc,argv)
struct iface *iface;
int argc;
char *argv[];
{
if(argc < 1){
printf("%d\r\n",Asy[iface->dev].speed);
return 0;
}
return asy_speed(iface->dev,atoi(argv[0]));
}
/* Set asynch line speed */
int
asy_speed(dev,speed)
int16 dev;
int speed;
{
register unsigned base;
register int divisor;
char i_state;
if(speed == 0 || dev >= Nasy)
return -1;
base = Asy[dev].addr;
Asy[dev].speed = speed;
divisor = BAUDCLK / (long)speed;
i_state = dirps();
/* Purge the receive data buffer */
(void)inportb(base+RBR);
if (Asy[dev].is_16550) /* clear tx+rx fifos */
outportb(base+FCR,(char) FIFO_SETUP);
/* Turn on divisor latch access bit */
setbit(base+LCR,LCR_DLAB);
/* Load the two bytes of the register */
outportb(base+DLL,(char)(divisor & 0xff)); /* Low byte */
outportb(base+DLM,(char)((divisor >> 8) & 0xff)); /* Hi byte */
/* Turn off divisor latch access bit */
clrbit(base+LCR,LCR_DLAB);
restore(i_state);
return 0;
}
/* Send a buffer to serial transmitter */
void
asy_output(dev,buf,cnt)
unsigned dev;
char *buf;
unsigned short cnt;
{
register struct dma *dp;
unsigned base;
char i_state;
if(dev >= Nasy)
return;
base = Asy[dev].addr;
dp = &Asy[dev].dma;
i_state = dirps();
if(dp->flags){
restore(i_state);
return; /* Already busy */
}
dp->data = buf;
dp->cnt = cnt;
dp->flags = 1;
/* Enable transmitter buffer empty interrupt and simulate
* an interrupt; this will get things rolling.
*/
setbit(base+IER,IER_TxE);
asytxint(dev);
restore(i_state);
}
/* Blocking read from asynch line
* Returns count of characters read
*/
char
get_asy(dev)
int16 dev;
{
char i_state;
register struct fifo *fp;
char c;
fp = &Asy[dev].fifo;
i_state = dirps();
while(fp->cnt == 0)
pwait(fp);
fp->cnt--;
restore(i_state);
c = *fp->rp++;
if(fp->rp >= &fp->buf[fp->bufsize])
fp->rp = fp->buf;
return c;
}
/* Interrupt handler for 8250 asynch chip */
void
asyint(dev)
unsigned dev;
{
register unsigned base;
register char iir;
struct fifo *fp;
int cnt = 0;
base = Asy[dev].addr;
fp = &Asy[dev].fifo;
while(((iir = inportb(base+IIR)) & IIR_IP) == 0){
switch(iir & IIR_ID){
case IIR_RDA: /* Receiver interrupt */
cnt += asyrxint(dev);
break;
case IIR_THRE: /* Transmit interrupt */
asytxint(dev);
break;
}
/* should happen at end of a single slip packet */
if (iir & IIR_FIFO_TIMEOUT)
Asy[dev].fifotimeouts++;
}
if(cnt != 0)
psignal(fp,1);
}
/* Process 8250 receiver interrupts */
static int
asyrxint(dev)
unsigned dev;
{
unsigned base;
register struct fifo *fp;
struct asy *asyp;
char c;
int cnt = 0;
char lsr;
asyp = &Asy[dev];
base = asyp->addr;
fp = &asyp->fifo;
for(;;){
lsr = inportb(base+LSR);
if(lsr & LSR_OE)
asyp->overrun++;
if(lsr & LSR_DR){
asyp->rxchar++;
c = inportb(base+RBR);
/* If buffer is full, we have no choice but
* to drop the character
*/
if(fp->cnt != fp->bufsize){
*fp->wp++ = c;
if(fp->wp >= &fp->buf[fp->bufsize])
/* Wrap around */
fp->wp = fp->buf;
fp->cnt++;
cnt++;
} else
asyp->rxdropped++;
} else
break;
}
asyp->rxints++;
return cnt;
}
/* Handle 8250 transmitter interrupts */
static void
asytxint(dev)
unsigned dev;
{
register struct dma *dp;
register unsigned base;
register int count;
struct asy *asyp;
asyp = &Asy[dev];
base = asyp->addr;
dp = &asyp->dma;
if(!dp->flags){
/* "Shouldn't happen", but disable transmit
* interrupts anyway
*/
clrbit(base+IER,IER_TxE);
return; /* Nothing to send */
}
/* if it's a 16550, load up to 16 chars into the tx hw fifo
* at once. With an 8250, it can be on char at most.
*/
if (asyp->is_16550) {
count = min(dp->cnt,OUTPUT_FIFO_SIZE);
/* 16550: LSR_THRE will drop after the first char loaded
* so we can't look at this bit to determine if the hw fifo is
* full. There seems to be no way to determine if the tx fifo
* is full (any clues?). So we should never get here while the
* fifo isn't empty yet.
*/
asyp->txchar += count;
dp->cnt -= count;
while(count--)
outportb(base+THR,*dp->data++);
if(dp->cnt == 0){
dp->flags = 0;
/* Disable transmit interrupts */
clrbit(base+IER,IER_TxE);
asytxdone(dev);
}
} else { /* 8250 */
while(inportb(base+LSR) & LSR_THRE){
asyp->txchar++;
outportb(base+THR,*dp->data++);
if(--dp->cnt == 0){
dp->flags = 0;
/* Disable transmit interrupts */
clrbit(base+IER,IER_TxE);
asytxdone(dev);
break;
}
}
}
}
int
stxrdy(dev)
int16 dev;
{
return(!Asy[dev].dma.flags);
}
int
doasystat(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct asy *asyp;
for(asyp = Asy;asyp < &Asy[Nasy];asyp++){
printf("%s: %s",asyp->iface->name,
asyp->is_16550 ? "NS16550A" : "");
printf(" RX: int %lu chr %lu ovrn %lu",
asyp->rxints,asyp->rxchar,asyp->overrun);
if(asyp->is_16550)
printf(" fifotim %lu",asyp->fifotimeouts);
printf(" TX: int %lu chr %lu\n",asyp->txints,asyp->txchar);
}
return 0;
}